﻿//Copyright (C) <2013>  <jonathan cleeve norton> All Rights Reserved 
//Contact jon.norton@fin-plus.co.uk website <http://www.fin-plus.co.uk/>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.ComponentModel;
using System.Windows.Threading;
using FinPlusCommon;
using log = Logger.Logger;
using u = FinPlusUtility.Utility;

namespace FinPlusWPFControls
{
    public partial class FinPlusGroupGrid : FinPlusControl
    {
        public override string[] States { get { return new string[] { "Height", "Width", "Left", "Top", "ColumnState", "Filterbars", "FilteredOut", }; } }
        public override string[] TestStates { get { return new string[] { "TestString", "ViewModelTest", "ViewString", "CellModelTest", "IsValid", "ColumnState", "ItemsCount" }; } }
        public override IFunctions Adaptor { get { return _adaptor; } }
        public override string TestString { get { return _adaptor.ToString(); } }
        
        public FuncDynamics DynFuncs { get; private set; }
        public CollectionViewSource ViewSource { get; private set; }
        public string CellModelTest { get { return CellModelToString(); } }
        public string ViewModelTest { get { return DynFuncs.ToFunctions().ToString(); } }
        public int ItemsCount { get { return dataGrid.Items.Count; } }
        public string Filterbars { get { return _filterbarTray.Filterbars; } set { _filterbarTray.Filterbars = value; HeaderCheckBoxFilterbars(); } }
        public string FilteredOut { get { return _filterbarTray.FilteredOut; } set { _filterbarTray.FilteredOut = value; } }
        public string ColumnState { get { return _colHelper.GetColState(dataGrid); } set { _colHelper.SetColState(dataGrid, _colSelectBar, value); } }

        private enum DockControls { FinPlusConnectService, FinPlusConnectXML, FinPlusFilterbarTray, FinPlusTransform, FinPlusColSelectBar, FinPlusColOnOffBar, FinPlusColButtonBar, FinPlusColValueShiftBar, FinPlusContextMenu, FinPlusMenuBar, FuncDynamics, Exception, Default }
        private enum Params { Id, FunctionName, Tag, StringFormat, Type, InputMask, QuoteInstrument, BookedTrade }
        private FinPlusStatusBar _statusBar;
        private IFinPlusConnect _conn;
        private FinPlusFilterbarTray _filterbarTray;
        private FinPlusColValueShiftBar _colValueShiftBar;
        private FinPlusColSelectBar _colSelectBar;
        private FinPlusColButtonBar _buttonBar;
        private FinPlusColOnOffBar _colOnOffBar;
        private FinPlusContextMenu _contextMenu;
        private FinPlusMenuBar _menuBar;
        private readonly FinPlusGridColHelper _colHelper;
        private readonly IFunctions _adaptor;
 
        //construct
        public FinPlusGroupGrid(object[] links, IFinPlusComp colHelper, bool makeRowVirtual, bool makeColVirtual)
        {
            InitializeComponent();
            _adaptor = new Functions();

            dataGrid.EnableRowVirtualization = makeRowVirtual;
            dataGrid.EnableColumnVirtualization = makeColVirtual;

            _colHelper = (FinPlusGridColHelper)colHelper;
            _colHelper.Link(this, Filterbar_Add, Filterbar_Remove);

            if (links != null)
                foreach (var link in links)
                    Link(link, string.Empty);

            IsValid = true;
        }

        //common control interface
        public override void CmdExecute(IFunction command)
        {
            try
            {
                CmdCapture(command);

                switch ((Cmds)Enum.Parse(typeof(Cmds), command.Name, true))
                {
                    case Cmds.Refresh: ViewSource.View.Refresh(); break;
                    case Cmds.AddFilterbar: AddFilterbar(command[Params.Tag.ToString()].ToString()); break;
                    case Cmds.RemoveFilterbar: RemoveFilterbar(command[Params.Tag.ToString()].ToString()); break;
                    case Cmds.ColSelection: AddColSelection(command[Params.Tag.ToString()].ToString()); break;
                    case Cmds.ColOn: ColOnOff(command, true); break;
                    case Cmds.ColOff: ColOnOff(command, false); break;
                    case Cmds.ColButton: ColButton(command); break;
                    case Cmds.ColValueShiftUp: ColValueShift(command, true); break;
                    case Cmds.ColValueShiftDown: ColValueShift(command, false); break;
                    case Cmds.ColValShiftLevel: ColValueShiftLevel(command); break;
                    case Cmds.NewColumn: AddColumn(command[Params.Tag.ToString()].ToString()); break;
                    default: throw new Exception(string.Format("event not recognised {0}", command.Name));
                }
            }
            catch (CmdExecuteException e)
            {
                IsValid = log.Log(Logger.Level.Error, Id, Config, e.Message, e);
            }
        }

        public override void Dispose()
        {
            try
            {
                if(_conn != null)
                    _conn.Dispose();
                if (_colHelper != null)
                    _colHelper.Dispose();
            }
            catch (Exception e)
            {
                log.Log(Logger.Level.Error, Id, Config, e.Message, e);
            }
        }

        //public
        public void AddFunction(IFinPlusFuncDynamic func)
        {
            try
            {
                DynFuncs = new FuncDynamics();
                DynFuncs.Add(func);
                ViewSource = new CollectionViewSource() { Source = DynFuncs };
                dataGrid.DataContext = ViewSource.View;
                dataGrid.IsEnabled = false;
                AddColumns();
            }
            catch (Exception e)
            {
                IsValid = log.Log(Logger.Level.Error, Id, Config, e.Message, e);
                toolbarTray.Children.Add(new FinPlusErrorDisplay(e));
            }
        }

        //private 
        private void Link(object control, string dock)
        {
            try
            {
                switch (u.EnumParse<DockControls>(control.GetType().Name, DockControls.Default))
                {
                    case DockControls.FinPlusConnectService: AddConnection((IFinPlusConnect)control); break;
                    case DockControls.FinPlusFilterbarTray: AddFilterbarTray((FinPlusFilterbarTray)control, dock); break;
                    case DockControls.FinPlusColSelectBar: AddColSelector((FinPlusColSelectBar)control, dock); break;
                    case DockControls.FinPlusColOnOffBar: AddColOnOff((FinPlusColOnOffBar)control, dock); break;
                    case DockControls.FinPlusColButtonBar: AddButtonBar((FinPlusColButtonBar)control, dock); break;
                    case DockControls.FinPlusColValueShiftBar: AddColValueShift((FinPlusColValueShiftBar)control, dock); break;
                    case DockControls.FinPlusTransform: AddTransform((FinPlusTransform)control); break;
                    case DockControls.FinPlusContextMenu: AddContextMenu((FinPlusContextMenu)control); break;
                    case DockControls.FinPlusMenuBar: AddMenuBar((FinPlusMenuBar)control); break;
                    case DockControls.FuncDynamics: AddFuncDynamics((FuncDynamics)control); break;
                    case DockControls.Exception: toolbarTray.Children.Add(new FinPlusErrorDisplay((Exception)control)); break;
                    case DockControls.Default: toolbarTray.Children.Add(DockSet((UserControl)control, dock)); break;
                }
            }
            catch (Exception e)
            {
                IsValid = log.Log(Logger.Level.Error, Id, Config, e.Message, e);
                toolbarTray.Children.Add(new FinPlusErrorDisplay(e));
            }
        }

        private FinPlusStatusBar GetSatusbar()
        {
            if (_statusBar == null)
            {
                var app = ControlSearch.FindVisualParent<FinPlusView>(this);
                if (app != null)
                    return _statusBar = ControlSearch.FindChild<FinPlusStatusBar>(app);
                
            }
            return _statusBar;
        }

        private void AddConnection(IFinPlusConnect conn)
        {
            try
            {
                _conn = conn;
                DynFuncs = conn.GetFuncsTransposed();
                ViewSource = new CollectionViewSource() { Source = DynFuncs };
                dataGrid.ItemsSource = ViewSource.View;
                dataGrid.IsEnabled = _conn.CanWrite;
                DynFuncs.ColumnChanged += Column_New;
                AddColumns();
            }
            catch (Exception e)
            {
                IsValid = log.Log(Logger.Level.Error, Id, Config, e.Message, e);
                toolbarTray.Children.Add(new FinPlusErrorDisplay(e));
            }
        }

        private void AddFuncDynamics(FuncDynamics funcDynamic)
        {
            try
            {
                DynFuncs = funcDynamic;
                ViewSource = new CollectionViewSource() { Source = DynFuncs };
                dataGrid.ItemsSource = ViewSource.View;
                dataGrid.IsEnabled = true;
                DynFuncs.ColumnChanged += Column_New;
                AddColumns();
            }
            catch (Exception e)
            {
                IsValid = log.Log(Logger.Level.Error, Id, Config, e.Message, e);
                toolbarTray.Children.Add(new FinPlusErrorDisplay(e));
            }
        }

        private void AddColSelector(FinPlusColSelectBar colSelector, string dock)
        {
            try
            {
                _colSelectBar = colSelector;
                _colSelectBar.ColSelection += ColSelector_OnSelection;
                _adaptor.Add(new Function() { Object = colSelector }, true, false);
                buttonbarTray.Children.Add(DockSet(_colSelectBar, dock));
            }
            catch (Exception e)
            {
                IsValid = log.Log(Logger.Level.Error, Id, Config, e.Message, e);
                toolbarTray.Children.Add(new FinPlusErrorDisplay(e));
            }
        }

        private void AddColOnOff(FinPlusColOnOffBar colOnOff, string dock)
        {
            try
            {
                _colOnOffBar = colOnOff;
                _colOnOffBar.ColOnOff += ColOnOff_Click;
                _adaptor.Add(new Function() { Object = colOnOff }, true, false);
                buttonbarTray.Children.Add(DockSet(_colOnOffBar, dock));
            }
            catch (Exception e)
            {
                IsValid = log.Log(Logger.Level.Error, Id, Config, e.Message, e);
                toolbarTray.Children.Add(new FinPlusErrorDisplay(e));
            }
        }

        private void AddButtonBar(FinPlusColButtonBar buttonBar, string dock)
        {
            try
            {
                _buttonBar = buttonBar;
                _buttonBar.ColButtonClick += ColButton_Click;
                buttonbarTray.Children.Add(DockSet(buttonBar, dock));
            }
            catch (Exception e)
            {
                IsValid = log.Log(Logger.Level.Error, Id, Config, e.Message, e);
                toolbarTray.Children.Add(new FinPlusErrorDisplay(e));
            }
        }

        private void AddColValueShift(FinPlusColValueShiftBar colValueShift, string dock)
        {
            try
            {
                _colValueShiftBar = colValueShift;
                _colValueShiftBar.ColValueShift += ColValueShift_Click;
                _adaptor.Add(new Function() { Object = colValueShift }, true, false);
                buttonbarTray.Children.Add(DockSet(_colValueShiftBar, dock));
            }
            catch (Exception e)
            {
                IsValid = log.Log(Logger.Level.Error, Id, Config, e.Message, e);
                toolbarTray.Children.Add(new FinPlusErrorDisplay(e));
            }
        }

        private void AddFilterbarTray(FinPlusFilterbarTray filterbarTray, string dock)
        {
            try
            {
                using (ViewSource.View.DeferRefresh())
                {
                    _filterbarTray = filterbarTray;
                    if (ViewSource == null)
                        throw new Exception("Filtertray has no viewsource");
                    _filterbarTray.AddConnection(DynFuncs);
                    _filterbarTray.FilterUpdate += View_Refresh;
                    ViewSource.View.Filter = obj => { return Filter(obj); };
                }
                toolbarTray.Children.Add(DockSet(_filterbarTray, dock));
            }
            catch (Exception e)
            {
                IsValid = log.Log(Logger.Level.Error, Id, Config, e.Message, e);
                toolbarTray.Children.Add(new FinPlusErrorDisplay(e));
            }
        }

        private void AddTransform(FinPlusTransform transform)
        {
            buttonbarTray.Children.Add(transform);
        }

        private void AddContextMenu(FinPlusContextMenu contextMenu)
        {
            _contextMenu = contextMenu;
            dataGrid.ContextMenu = contextMenu.Menu;
        }

        private void AddMenuBar(FinPlusMenuBar menuBar)
        {
            _menuBar = menuBar;
            buttonbarTray.Children.Add(menuBar);
        }

        private bool AddFilterbar(string name)
        {
            using (ViewSource.View.DeferRefresh())
            {
                if (_filterbarTray.HasFilterbar(name)) return false;

                var filterbar = new FinPlusFilterbar(name);
                _filterbarTray.AddFilterbar(new FinPlusFilterbar(name));
                _adaptor.Add(new Function() { Object = filterbar }, true, false);
            }
            return true;
        }

        private void AddColumns()
        {
            try
            {
                using (ViewSource.View.DeferRefresh())
                {
                    foreach (var func in DynFuncs)
                        foreach (var name in func.GetDynamicMemberNames())
                            AddColumn(name, func.GetType(name));
                }
            }
            catch (Exception e)
            {
                IsValid = log.Log(Logger.Level.Error, Id, Config, e.Message, e);
                toolbarTray.Children.Add(new FinPlusErrorDisplay(e));
            }
        }

        private void AddColumn(string name, string type = "ValueFormat")
        {
            var col = _colHelper.GetTemplate(name, type);
            if (col != null)
                dataGrid.Columns.Add(col);
        }

        private void AddColSelection(string name)
        {
            var col = dataGrid.Columns.Where(c => c.GetValue(TagProperty).ToString().Equals(name)).First();
            col.Visibility = col.Visibility == Visibility.Visible ? Visibility.Collapsed : Visibility.Visible;
        }

        private void ColOnOff(IFunction func, bool onOff)
        {
            try
            {
                var colName = func[Params.Tag.ToString()].ToString();

                foreach (var dynFunc in DynFuncs)
                    if (Filter(dynFunc) && GetPermission(dynFunc).CanWrite && !dynFunc.GetValue(colName).Equals(onOff))
                        dynFunc.SetValue(colName, onOff, User, DateTime.Now);
            }
            catch (Exception e)
            {
                IsValid = log.Log(Logger.Level.Error, Id, Config, e.Message, e);
            }
        }

        private void ColButton(IFunction func)
        {
            var s = func[Params.Tag.ToString()].ToString().Split(',');
            DynFuncs[0].SetValue(s[1], new Function(s[0]).ToString(), User, DateTime.Now);
        }

        private void ColValueShift(IFunction func, bool up)
        {
            try
            {
                var shift = func[Params.Tag.ToString()].ToString().Split(',');

                foreach (var dynFunc in DynFuncs)
                {
                    if (!Filter(dynFunc)) continue;

                    double shiftColVal, factor, minShift;

                    if (GetPermission(dynFunc).CanWrite && double.TryParse(dynFunc.GetValue(shift[0]).ToString(), out shiftColVal) && double.TryParse(shift[1], out factor) && double.TryParse(dynFunc.GetValue(shift[2]).ToString(), out minShift))
                        dynFunc.SetValue(shift[0], shiftColVal + (up ? factor : -factor) * minShift, User, DateTime.Now);
                }
            }
            catch (Exception e)
            {
                IsValid = log.Log(Logger.Level.Error, Id, Config, e.Message, e);
            }
        }

        private void ColValueShiftLevel(IFunction func)
        {
            try
            {
                var shift = func[Params.Tag.ToString()].ToString().Split(',');

                foreach (var dynFunc in _conn.GetFuncsTransposed())
                    if (Filter(dynFunc) && GetPermission(dynFunc).CanWrite && !dynFunc.GetValue(shift[0]).Equals(0.0))
                        dynFunc.SetValue(shift[0], 0.0, User, DateTime.Now);
            }
            catch (Exception e)
            {
                IsValid = log.Log(Logger.Level.Error, Id, Config, e.Message, e);
            }
        }

        private void RemoveFilterbar(string name)
        {
            _filterbarTray.RemoveFilterbar(name);
        }

        private bool Filter(object obj)
        {
            if (obj == null) return false;

            var func = (IFinPlusFuncDynamic)obj;
            foreach (KeyValuePair<string, FinPlusFilterbar> itemName in _filterbarTray.FilterOut)
                foreach (var itemValue in itemName.Value.FilterOut)
                    if (func.ContainsObject(itemName.Key, itemValue))
                        return false;

            return true;
        }

        private void HeaderCheckBoxFilterbars()
        {
            foreach (var kvp in _colHelper.ColMaps)
                if (_filterbarTray != null)
                    kvp.Value.FilterCheck.IsChecked = _filterbarTray.HasFilterbar(kvp.Key);
        }

        private string ViewString()
        {
            var s = new StringBuilder();

            for (int row = 0; row < dataGrid.Items.Count; row++)
            {
                for (int col = 0; col < dataGrid.Columns.Count; col++)
                {
                    //GetCell forces render
                    var cell = DataGridHelper.GetCell(dataGrid, row, col);
                    s.Append(cell.ToString());
                    if (col < dataGrid.Columns.Count - 1)
                        s.Append(",");
                }
                s.Append(";");
            }

            return s.ToString();
        }

        private string CellModelToString()
        {
            var s = new StringBuilder();

            for (int row = 0; row < dataGrid.Items.Count; row++)
            {
                for (int col = 0; col < dataGrid.Columns.Count; col++)
                {
                   //GetCell forces render
                   var cell = DataGridHelper.GetCell(dataGrid, row, col);
                   var presenter = (ContentPresenter)cell.Content;
                   var control = (FinPlusControl)presenter.ContentTemplate.FindName("root", presenter);
                   s.Append(control.TestString);
                   if (col < dataGrid.Columns.Count - 1)
                        s.Append(",");
                }
                s.Append(";");
            }

            return s.ToString();
        }

        //cmds
        private enum Cmds { Refresh, AddFilterbar, NewColumn, RemoveFilterbar, ColSelection, ColOn, ColOff, ColButton, ColValueShiftUp, ColValueShiftDown, ColValShiftLevel }

        //events
        private void View_Refresh(object s, IFunction func) { CmdExecute(func); }
        private void ColSelector_OnSelection(object s, IFunction func) { CmdExecute(func); }
        private void ColOnOff_Click(object s, IFunction func) { CmdExecute(func); }
        private void ColButton_Click(object s, IFunction func) { CmdExecute(func); }
        private void ColValueShift_Click(object s, IFunction func) { CmdExecute(func); } 
        private void Filterbar_Add(object s, RoutedEventArgs a) { CmdRouter(Cmds.AddFilterbar.ToString(), s); }
        private void Filterbar_Remove(object s, RoutedEventArgs a) { CmdRouter(Cmds.RemoveFilterbar.ToString(), s); }
        private void Lost_Focus(object s, RoutedEventArgs a) { dataGrid.SelectedIndex = -1; }
        private void Grid_Selected(object s, RoutedEventArgs a)
        {
            try
            {
                if (a.OriginalSource.GetType() != typeof(DataGridCell)) return;

                var grid = (DataGrid)s;
                grid.BeginEdit(a);

                if (GetSatusbar() != null)
                {
                    var total = 0.0;
                    var cells = dataGrid.SelectedCells;
                    foreach (var item in cells)
                        if (item != null)
                            total += Cells.GetValue(item);

                    _statusBar.total.Text = string.Format("Rows={0} Count={1} Σ={2}, μ={3}", dataGrid.Items.Count, cells.Count(), total.ToString("#,##0.######;-#,##0.######"), (total / cells.Count()).ToString("#,##0.######;-#,##0.######"));
                }
            }
            catch (Exception e)
            {
                log.Log(Logger.Level.Error, Id, Config, e.Message, e);
            }
        }
        private void Column_New(object s, PropertyChangedEventArgs a) 
        {
            var func = new Function(Cmds.NewColumn.ToString(), Id, User, -1, a.PropertyName);
            if (!CheckAccess())
               Dispatcher.Invoke(() =>  CmdExecute(func));
            else
               CmdExecute(func);
        }
   }
}